#!/usr/bin/env python
# Copyright(c) 2010 Nuttachat Wisittipanit. All rights reserved.

from minings import (Param,SVMpm,)

import xlwt,numpy,interval
from copy import deepcopy


class FormalReport(object):
    
    def __init__(self,**kwargs):
        #Process only one mainset at a time
        #Note. one mainset spawns 19 datasets
        self.output_file    = ''
        self.pthreshold     = 0.08
        #self.dset  -> {'gutmtpslhpcr_Colon_Lumen__Crohn_Colon__Crohn_Lumen': RESULTS isstance, ..}
        self.dset           = {}
        #self.dsort -> List of datasetname -> ['gutmtpslhpcr_Colon_Lumen__Crohn_Colon__Crohn_Lumen', ..] 
        self.dsort          = []
        self.__dict__.update(kwargs)
        
    def xcelRecord(self):
        #Add 2 sheets name 'SVM' and 'Metastats'
        #Excel properties
        self.SVM                = 'SVM'
        self.MTS                = 'Metastats'
        self.book               = xlwt.Workbook()
        self.sheet              = {}
        self.sheet[self.SVM]    = self.book.add_sheet(self.SVM,cell_overwrite_ok = True)
        self.sheet[self.MTS]    = self.book.add_sheet(self.MTS,cell_overwrite_ok = True)
        self.col_width          = 6000
        self.svmRecord()
        self.mstatRecord()
        self.saveBook()
    
    def svmRecord(self):      
        #I need to get for entire crohn get datasetname = 'gutmtpslhpcr_Entire__Crohn__Healthy Control'
        #                  entire ulcer get datasetname = 'gutmtpslhpcr_Entire__Healthy Control__Ulcerative Colitis'
        #Do SVM result of Crohn first
        self.s_list_a       = ['Entire','Ileum','Colon','Sigmoid','Lumen',]
        self.d_list         = ['Crohn','Ulcerative Colitis',]
        self.d_list_a       = ['Crohn','Ulcerative Colitis','Healthy Control',]
        self.s_list_b       = ['Ileum_Lumen','Colon_Lumen','Sigmoid_Lumen',]   
        self.vs_list        = ['Ileum vs Lumen','Colon vs Lumen','Sigmoid vs Lumen',]
        avg                 = ['Average',]     
        mainSVMlength       = 23
        mainSVMlengtha      = 10
        h                   = [2,4,10,16,]
        hi                  = [1,4,7,]
        hh                  = ['Class Type','FS_FULL','FS_RELIEF','FS_BOTH',]
        b                   = ['Positive','Negative','POS/NEG',]
        self.t_list         = ['FS_FULL','FS_RELIEF','FS_BOTH',]  
        t_hash              = {'FS_FULL'    : ['Full features',]        ,
                               'FS_RELIEF'  : ['NFS',]                  ,
                               'FS_BOTH'    : ['1st NFS','2nd NFS',]    ,
                               }
        c                   = ['Accuracy(%)','AUC','Sensitivity','Specificity','F-Measure',]
        cc                  = ['Accuracy','AUC','Sens','Spec','F1',] 
        y                   = ['Accuracy(%)','AUC','F-Measure',]
        ccc                 = ['Accuracy','AUC','F1',]
        #Assemble 
        f                   = [None,]
        #ff  -> (None,'Positive','Negative','POS/NEG',)
        ff                  = f + b
        for i in range(len(self.t_list)):
            #u  -> ('Full features',)
            u   = t_hash[self.t_list[i]]
            #uu -> ('Full features','Accuracy(%)','AUC','Sensitivity','Specificity','F-Measure',)
            uu  = u + c
            ff  = ff + uu
        #collect -> {'Entire':
        #                    {'Crohn__Healthy Control'             : 
        #                                                            {'FS_FULL'  : Collect instance,
        #                                                             'FS_RELIEF': Collect instance,
        #                                                             'FS_BOTH'  : Collect instance,
        #                                                            }
        #                     'Healthy Control__Ulcerative Colitis': 
        #                                                            {'FS_FULL'  : Collect instance,
        #                                                             'FS_RELIEF': Collect instance,
        #                                                             'FS_BOTH'  : Collect instance,
        #                                                            }
        #                    }, 
        #            'Ileum_Lumen':
        #                    {'Crohn_Ileum__Crohn_Lumen'           :
        #                                                            {'FS_FULL'  : Collect instance,
        #                                                             ..
        #                                                            },
        #                    }        
        #           }
        self.collect    = {}      
        for i in range(len(self.dsort)):
            #dname       -> 'gutmtpslhpcr_Entire__Healthy Control__Ulcerative Colitis,'gutmtpslhpcr_Colon_Lumen__Crohn_Colon__Crohn_Lumen'
            dname           = self.dsort[i]                
            #mainname    -> 'Entire'
            mainname        = '_'.join(dname.split('__')[0].split('_')[1:])
            #subname     -> 'Healthy Control__Ulcerative Colitis'
            subname         = '__'.join(dname.split('__')[1:])
            #result_obj  -> RESULTS instance
            result_obj      = self.dset[dname]                
            results_svm     = result_obj.results_svm
            results_mstat   = result_obj.results_mstat
            results         = results_svm.results # List of UniverseDATA instances
            mtres           = results_mstat.orgnresult
            try:
                self.collect[mainname]
            except (KeyError,):
                self.collect[mainname]      = {}
            self.collect[mainname][subname] = {}
            #subcol -> {'FS_FULL': Collect instance,..}
            subcol                          = self.collect[mainname][subname]
            for j in results:
                #j -> UniverseDATA instance
                udata       = j
                if   udata.atpm.num == -1:
                    type_   = 'FS_FULL'
                elif udata.atpm.num != -1 and udata.SVMdrtn == SVMpm.Drtn.NORM:
                    type_   = 'FS_RELIEF'
                elif udata.atpm.num != -1 and udata.SVMdrtn in (SVMpm.Drtn.B2UP,SVMpm.Drtn.T2DW,):
                    type_   = 'FS_BOTH'
                subcol[type_]                           = Collect()
                pn                                      = subname.split('__')
                clsnum                                  = udata.ttcls.split('@')[1]
                if 'Healthy Control' in pn:
                    haveHealthy                         = True
                    subcol[type_].Positive              = pn[0] if pn[0] != 'Healthy Control' else pn[1]
                    subcol[type_].Negative              = pn[1] if pn[1] == 'Healthy Control' else pn[0]
                else:
                    haveHealthy                         = False
                    subcol[type_].Positive              = clsnum.split(';')[0].split(':')[0]
                    subcol[type_].Negative              = clsnum.split(';')[1].split(':')[0]
                if haveHealthy:
                    pnum                                = clsnum.split(';')[0].split(':')[1] \
                                                          if clsnum.split(';')[0].split(':')[0] != 'Healthy Control' \
                                                          else clsnum.split(';')[1].split(':')[1]
                    nnum                                = clsnum.split(';')[0].split(':')[1] \
                                                          if clsnum.split(';')[0].split(':')[0] == 'Healthy Control' \
                                                          else clsnum.split(';')[1].split(':')[1]
                else:
                    pnum                                = clsnum.split(';')[0].split(':')[1]
                    nnum                                = clsnum.split(';')[1].split(':')[1]
                #p_n -> '10/14' number of Positive/Negative
                subcol[type_].p_n                       = '%s/%s' % (pnum,nnum)
                if udata.ttcls_w != None:
                    clsnum_w                            = udata.ttcls_w.split('@')[1]
                    if haveHealthy:
                        pnum_w                          = clsnum_w.split(';')[0].split(':')[1] \
                                                          if clsnum_w.split(';')[0].split(':')[0] != 'Healthy Control' \
                                                          else clsnum_w.split(';')[1].split(':')[1]
                        nnum_w                          = clsnum_w.split(';')[0].split(':')[1] \
                                                          if clsnum_w.split(';')[0].split(':')[0] == 'Healthy Control' \
                                                          else clsnum_w.split(';')[1].split(':')[1]
                    else:
                        pnum_w                          = clsnum_w.split(';')[0].split(':')[1]
                        nnum_w                          = clsnum_w.split(';')[1].split(':')[1]
                    subcol[type_].p_n_w                 = '%s/%s' % (pnum_w,nnum_w)
                else:
                    subcol[type_].p_n_w                 = None
                subcol[type_].nf1                       = str(len(udata.data.domain.attributes))
                subcol[type_].nf2                       = str(len(udata.SVMbestatts))
                subcol[type_].Accuracy                  = '%.2f' % (udata.SVMbestacc * 100)
                if udata.SVMstat != None:
                    subcol[type_].AUC                   = udata.SVMaucs.split(';')[0].split('@')[1]
                    for k in udata.SVMstat.split(';'):
                        if k.split('@')[0] != 'Healthy Control':
                            subcol[type_].TP            = k.split('@')[1].split(':')[0]
                            subcol[type_].FP            = k.split('@')[1].split(':')[1]
                            subcol[type_].FN            = k.split('@')[1].split(':')[2]
                            subcol[type_].TN            = k.split('@')[1].split(':')[3]
                            subcol[type_].Sens          = k.split('@')[1].split(':')[4]
                            subcol[type_].Spec          = k.split('@')[1].split(':')[5]
                            subcol[type_].PPV           = k.split('@')[1].split(':')[6]
                            subcol[type_].NPV           = k.split('@')[1].split(':')[7]
                            subcol[type_].F1            = k.split('@')[1].split(':')[8]
                            break        
                subcol[type_].mtres                     = mtres
                st                                      = subcol[type_]
                #Find the mean difference
                for i in mtres.MSTATinfo_tstat:
                    #i -> feature1
                    v               = mtres.MSTATinfo_tstat[i] 
                    g               = v[1] #v[1] = {class1:(xbar1,var1),class2:(xbar2,var2)}
                    #t_sort -> [(class1,xbar1),(class2,xbar2)] Sorted from high to low based on xbar
                    t_sort          = sorted([(u,g[u][0]) for u in g],lambda x,y: cmp(y[1],x[1])) 
                    st.md_sort[i]   = deepcopy(t_sort)
                    xbar            = [u[0] for u in v[1].values()] #[xbar1,xbar2]
                    #Find the mean difference
                    low             = min(xbar)
                    high            = max(xbar)
                    md              = (((high-low)/float(low)) * 100 )
                    st.md[i]        = md
                    #st.md_sort[i]       -> [(class1,xbar1),(class2,xbar2)]
                    #st.md_sort[i][0][0] -> class1 having the highest xbar
                    #hclass -> 'Crohn','Healchy Control','Crohn_Lumen','Crohn_Sigmoid'
                    hclass          = st.md_sort[i][0][0]
                    gh              = hclass.split('_')
                    if len(gh) == 1:
                        st.mdhigh[i]    = hclass
                    else:
                        #hclass -> 'Crohn_Lumen'
                        st.mdhigh[i]    = gh[1] #-> 'Lumen' or 'Sigmoid' 
        #Start Recording into excel
        for i in range(1,15):
            self.sheet[self.SVM].col(i).width    = self.col_width
        c_col               = 1
        c_row               = 1
        for i in self.d_list:
            #i -> 'Crohn'
            self.c_col      = c_col
            self.c_row      = c_row
            #Write the Column 1
            cntz            = 0
            for j in range(mainSVMlength):
                if j in h:
                    self.sheet[self.SVM].write(self.ri(),self.c_col,hh[cntz])
                    cntz    += 1
                else:
                    self.sheet[self.SVM].write(self.ri(),self.c_col,None)
            #Write the column 2
            self.c_col      = c_col + 1
            self.c_row      = c_row
            for j in ff:
                self.sheet[self.SVM].write(self.ri(),self.c_col,j)
            self.c_col      = c_col + 2
            self.c_row      = c_row
            #Write column 3 (Entire) 
            for j in self.s_list_a:
                #j -> 'Entire','Ileum' or 'Colon'
                self.sheet[self.SVM].write(self.ri(),self.c_col,j)
                for k in self.collect[j]:
                    #k -> 'Crohn__Healthy Control'
                    #p -> ['Crohn','Healthy Control']
                    p = k.split('__')
                    if i in p:
                        #i -> Crohn                    
                        for m in self.t_list:
                            #m  -> 'FS_FULL','FS_RELIEF' or 'FS_BOTH'
                            #cl -> Collect instance
                            cl = self.collect[j][k][m]
                            if   m in ('FS_FULL',):
                                self.sheet[self.SVM].write(self.ri(),self.c_col,cl.Positive)
                                self.sheet[self.SVM].write(self.ri(),self.c_col,cl.Negative)
                                self.sheet[self.SVM].write(self.ri(),self.c_col,cl.p_n)
                                self.sheet[self.SVM].write(self.ri(),self.c_col,cl.nf1)
                            elif m in ('FS_RELIEF',):
                                self.sheet[self.SVM].write(self.ri(),self.c_col,cl.nf1)
                            elif m in ('FS_BOTH',):
                                self.sheet[self.SVM].write(self.ri(),self.c_col,cl.nf1)
                                self.sheet[self.SVM].write(self.ri(),self.c_col,cl.nf2)
                            for n in cc:
                                #n -> 'Accurach','AUC','Sens' or 'Spec'
                                self.sheet[self.SVM].write(self.ri(),self.c_col,getattr(cl,n))
                #Move Column
                self.ci()
                self.c_row  = c_row
            c_col = 1 
            c_row = 1 + mainSVMlength + 1
        c_col       = 1
        c_row       = 1 + (2 * mainSVMlength) + 2
        #for Ileum vs Lumen - (Crohn_Ileum vs Crohn_Lumen) and (Ulcerative Colitis_Ileum vs Ulcerative Colitis_Lumen) and 
        #                     (Healthy Control_Ileum vs Healthy Control_Lumen)
        fff         = f + y
        for i in range(2):
            fff = fff + y
        self.c_col  = c_col
        self.c_row  = c_row
        cntz        = 0
        for i in range(mainSVMlengtha):            
            if i in hi:
                self.sheet[self.SVM].write(self.c_row,self.ci(),self.d_list_a[cntz])
                cntz    += 1
            else:
                self.sheet[self.SVM].write(self.c_row,self.ci(),None)
        self.ri()
        self.c_col  = c_col
        for i in fff:
            self.sheet[self.SVM].write(self.c_row,self.ci(),i)
        self.ri()
        self.c_col  = c_col
        #average -> {'Crohn': {'Accuracy': [x,y,z],'AUC': x,'F1': x}, ..}
        average = {}
        for u in range(len(self.vs_list)):
            i = self.s_list_b[u]
            #i -> 'Ileum_Lumen','Colon_Lumen' or 'Sigmoid_Lumen'
            self.sheet[self.SVM].write(self.c_row,self.ci(),self.vs_list[u])
            #axa -> {'Crohn':'Crohn_Ileum__Crohn_Lumen','Ulcerative Colitis': 'Ulcerative Colitis_Ileum__Ulcerative Colitis_Lumen',..}
            axa     = {}
            vs_keys = self.collect[i].keys()
            for j in vs_keys:
                #j  -> 'Crohn_Ileum__Crohn_Lumen'
                #aj -> 'Crohn'
                aj      = j.split('__')[0].split('_')[0]
                axa[aj] = j
            for j in self.d_list_a:
                #j -> 'Crohn','Ulcerative Colitis' or 'Healthy Control'
                try:
                    average[j]
                except (KeyError,):
                    average[j]  = {}
                #jj -> 'Crohn_Ileum__Crohn__Lumen' 
                jj          = axa[j]                
                for m in self.collect[i][jj]:
                    #m -> 'FS_FULL'
                    if m != 'FS_RELIEF':
                        continue
                    cl  = self.collect[i][jj][m]
                    for n in ccc:
                        #n -> 'Accuracy','AUC' or 'F1'
                        try:
                            average[j][n]
                        except (KeyError,):
                            average[j][n] = [] 
                        value   = getattr(cl,n)
                        #average[j][n] -> [x,y,z]
                        average[j][n].append(float(value))
                        self.sheet[self.SVM].write(self.c_row,self.ci(),value)
            self.ri()
            self.c_col  = c_col
        self.sheet[self.SVM].write(self.c_row,self.ci(),avg[0])
        #Compile the average
        for i in self.d_list_a:
            #i -> 'Crohn'
            for j in ccc:
                #j      -> 'Accuracy'
                #values -> [x,y,z]
                values  = average[i][j]
                avgv    = numpy.mean(values)
                uavgv   = '%.2f' % avgv
                self.sheet[self.SVM].write(self.c_row,self.ci(),uavgv)
        
    def mstatRecord(self):
        MTS         = self.MTS
        #As I said axaxa
        row_list    = ['Feature','P-Value']
        s1          = 0 #Size of Crohn table
        s2          = 0 #Size of Ulcerative Colitis table
        mlength     = 3
        c_col       = 1
        c_row       = 1
        #Start Recording into excel
        for i in range(1,15):
            self.sheet[MTS].col(i).width    = self.col_width
        for i in self.d_list:
            #i -> 'Crohn' or 'Ulcerative Colitis'
            self.c_col  = c_col
            self.c_row  = c_row        
            maxp        = [] #[2,3,4,0] the number of features for each site    
            for j in self.s_list_a:
                #j -> 'Entire','Ileum' or 'Lumen'
                self.sheet[MTS].write(self.ri(),self.c_col,j)
                self.sheet[MTS].write(self.ri(),self.c_col,i)
                self.sheet[MTS].write(self.c_row,self.c_col     ,row_list[0]) # 'Feature'
                self.sheet[MTS].write(self.c_row,self.c_col + 1 ,row_list[1]) # 'P-Value'
                self.ri()                
                for k in self.collect[j]:
                    #k -> 'Crohn__Healthy Control'
                    #p -> ['Crohn','Healthy Control']
                    p = k.split('__')
                    if i in p:
                        #i -> Crohn
                        for m in self.t_list:
                            #m  -> 'FS_FULL','FS_RELIEF' or 'FS_BOTH'
                            #cl -> Collect instance
                            cl              = self.collect[j][k][m]
                            if m not in ('FS_FULL',):
                                #Get only 'FS_FULL' since mtres for all in self.t_list are the same
                                continue
                            mtres           = cl.mtres
                            #pselect -> [(feature1,p1),(feature2,p2), ..]
                            pselect         = [u for u in mtres.MSTATp_sort_fisher \
                                               if u[1] in interval.Interval(0,self.pthreshold,
                                                                            lower_closed = True,
                                                                            upper_closed = True)] \
                                               if mtres.MSTATp_sort_fisher != None else []
                            maxp.append(len(pselect))
                            temprow         = self.c_row
                            for n in pselect:
                                self.sheet[MTS].write(temprow,self.c_col        ,n[0])
                                self.sheet[MTS].write(temprow,self.c_col + 1    ,'%.4f' % n[1])
                                temprow     += 1                
                self.ci()
                self.ci()
                self.c_row = c_row
            maxn    = max(maxp)
            if i == self.d_list[0]:
                s1  = maxn
            else:
                s2  = maxn
            c_col   = 1
            c_row   = 1 + mlength + maxn + 1
        self.c_col  = c_col
        self.c_row  = 1 + (2 * mlength) + s1 + s2 + 4        
        #Do mucosa vs lumen
        fff         = [None,] + self.d_list_a
        for i in fff:
            self.sheet[MTS].write(self.c_row,self.ci(),i)
        self.c_col  = c_col
        self.ri()
        c_col       = 1
        c_row       = self.c_row 
        for u in range(len(self.vs_list)):
            self.c_col  = c_col
            self.c_row  = c_row
            i           = self.s_list_b[u]
            #i          -> 'Ileum_Lumen','Colon_Lumen' or 'Sigmoid_Lumen'
            #vs_list[0] -> 'Ileum vs Lumen'
            self.sheet[MTS].write(self.c_row,self.ci(),self.vs_list[u])
            #axa        -> {'Crohn':'Crohn_Ileum__Crohn_Lumen','Ulcerative Colitis': 'Ulcerative Colitis_Ileum__Ulcerative Colitis_Lumen',..}
            axa     = {}
            vs_keys = self.collect[i].keys()
            maxvs   = []
            for j in vs_keys:
                #j  -> 'Crohn_Ileum__Crohn_Lumen'
                #aj -> 'Crohn'
                aj      = j.split('__')[0].split('_')[0]
                axa[aj] = j
            for j in self.d_list_a:
                #j  -> 'Crohn','Ulcerative Colitis' or 'Healthy Control'
                #jj -> 'Crohn_Ileum__Crohn__Lumen' 
                jj      = axa[j]                
                for m in self.collect[i][jj]:
                    #m -> 'FS_FULL'
                    if m != 'FS_FULL':
                        continue
                    cl              = self.collect[i][jj][m]
                    mtres           = cl.mtres
                    #pselect -> [(feature1,p1),(feature2,p2), ..]
                    pselect         = [u for u in mtres.MSTATp_sort_fisher \
                                       if u[1] in interval.Interval(0,self.pthreshold,
                                                                    lower_closed = True,
                                                                    upper_closed = True)] \
                                       if mtres.MSTATp_sort_fisher != None else []
                    maxvs.append(len(pselect))
                    temprow         = self.c_row
                    for n in pselect:
                        fname       = n[0]
                        pv          = n[1]
                        mdhigh      = cl.mdhigh[fname]
                        dis         = '%s|%.4f|%s' % (fname,pv,mdhigh,)
                        self.sheet[MTS].write(temprow,self.c_col,dis)
                        temprow     += 1
                self.ci()
            c_col = 1
            if max(maxvs) > 0:
                c_row = self.c_row + max(maxvs)
            else:
                #if max(maxvs) == 0
                c_row = self.c_row + 1
    
    def saveBook(self):
        self.book.save(self.output_file)             
                
    def ci(self):
        self.c_col += 1
        #Increase and return current column
        return self.c_col - 1
    
    def ri(self):
        self.c_row += 1
        return self.c_row - 1
        
            
class Collect(object):
    
    def __init__(self,**kwargs):
        #These attributes are used to write in excel
        self.Positive   = ''
        self.Negative   = ''
        self.p_n        = ''
        self.p_n_w      = ''
        self.nf1        = '' #Number of features 1st stage
        self.nf2        = '' #Number of features 2nd stage
        self.Accuracy   = ''
        self.AUC        = ''
        self.TP         = ''
        self.FP         = ''
        self.FN         = ''
        self.TN         = ''
        self.Sens       = ''
        self.Spec       = ''
        self.PPV        = ''
        self.NPV        = ''
        self.F1         = ''
        self.dname      = '' #datasetname
        self.mtres      = '' #UniverseData of Metastats result
        self.md         = {} #{feature1:md,feature2:md .. }
        self.md_sort    = {} #{feature1:[(class1,xbar1),(class2,xbar2)], .. }
        self.mdhigh     = {} #{feature1: Name of the class having high xbar, ..}
        self.__dict__.update(kwargs)

            